#include <sstream>
#include "imglib/hexcode.h"
#include "imglib/bitmap.h"
#include "imglib/imageclass.h"
#include "imglib/gdiplus.h"
#include "xml/stream.h"

namespace std { struct input_iterator_tag; }

namespace img {

template <class R>
std::ostream & hex_encode(R const & r, std::ostream & s)
{
	std::ostringstream bin;

	// PNG has a specific passion of seeking the output stream 
	// before writing which leads to a stream failure.
	// So we just need to put something in first.
	bin << '\0';
	bin.seekp(0);
	bin << img::gdiplus(r, img::png_format); //without alpha-channel
	std::string binstring = bin.str();

	std::copy(
		binstring.begin(), binstring.end(),
		ostream_hex_iterator(s));
	return s;
}

template <class R>
void hex_encode(R const & r, std::string & hexstring)
{
	std::ostringstream hex;
	hex_encode(r, hex);
	hexstring = hex.str();
}

template <class R>
std::istream & hex_decode(R & r, std::istream & s)
{
	std::string binstring;

	std::copy(
		istream_hex_iterator(s), istream_hex_iterator(),
		std::inserter(binstring, binstring.end()));

	std::istringstream bin(binstring);
	bin >> img::gdiplus(r);
	return s;
}

template <class R>
void hex_decode(R & r, std::string const & hexstring)
{
	std::istringstream hex(hexstring);
	hex_decode(r, hex);
}

template <class R>
xml::stream & operator >> (xml::stream & s, R & b)
{
	using namespace xml;
	string hexstring;
	assert (s.in());
	s || def_attr("encoded", hexstring, string());
	if (!hexstring.empty())
		hex_decode(b, hexstring);
	else
		b.clear();
	return s;
}

template <class R>
xml::stream & operator << (xml::stream & s, R const & b)
{
	using namespace xml;
	assert (s.out());
	if (!b.empty()) {
		string hexstring;
		hex_encode(b, hexstring);
		s || attr("encoded", hexstring);
	}
	return s;
}

template <class R>
xml::stream & serialize(xml::stream & s, R & b)
{
	if (s.in())
		return s >> b;
	else
		return s << b;
}

xml::stream & operator || (xml::stream & s, bitmap & b)
	{ return serialize(s, b); }

xml::stream & operator || (xml::stream & s, mono_byte_image & b)
	{ return serialize(s, b); }

xml::stream & operator || (xml::stream & s, small_byte_image & b)
	{ return serialize(s, b); }

xml::stream & operator || (xml::stream & s, byte_image & b)
	{ return serialize(s, b); }

} //namespace img
